<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Vue的响应式2</title>
</head>
<body>
  <div id="app">
    <input type="text" v-model="message"/>
    {{message}}
  </div>

<script>
  class Vue {
    constructor(options) {
      //1、保存数据
      this.$options = options;
      this.$data = options.data;
      this.$el = options.el;

      //2、将data添加到响应式系统中
      new Observer(this.$data)

      //3、代理this.$data的数据
      Object.keys(this.$data).forEach(key => {
        this._proxy(key)
      })

      //4、处理el
      new Compiler(this.$el, this)
    }
    _proxy(key) {
      Object.defineProperty(this, key, {
        configurable: true,
        enumerable: true,
        set(newValue) {
          this.$data[key]=newValue
        },
        get() {
          return this.$data[key]
        }
      })
    }
  }
  class Observer {
    constructor(data) {
      this.data=data;
      Object.keys(data).forEach(key => {
        this.defineReactive(this.data, key, data[key])
      })
    }
    defineReactive(data, key, val) {
      const dep=new Dep()
      Object.defineProperty(data, key, {
        enumerable: true,
        configurable: true,
        get() {
          if (Dep.target) {
            dep.addSub(Dep.target)
          }
          return val
        },
        set(newValue) {
          if (newValue === val) {
            return
          }
          val=newValue
          dep.notify()
        }
      })
    }
  }

  class Dep {
    constructor() {
      this.subs = []
    }
    addSub(sub) {
      this.subs.push(sub)
    }
    notify() {
      this.subs.forEach(sub => {
        sub.update()
      })
    }
  }

  class Watcher {
    constructor(node, name, vm) {
      this.node =node;
      this.name= name;
      this.vm=vm;
      Dep.target=this;
      this.update();
      Dep.target=null;
    }
    update() {
      this.node.nodeValue = this.vm[this.name]
    }
  }

  const reg=/\{\{(.+)\}\}/
  class Compiler {
    constructor(el, vm) {
      this.el=document.querySelector(el)
      this.vm=vm
      //创建片段
      this.frag=this._createFragment()
      this.el.appendChild(this.frag)
    }
    //让片段对象和属性形成映射关系
    _createFragment() {
      const frag = document.createDocumentFragment()

      let child;
      while (child===this.el.firstChild) {
        this._compile(child)
        frag.appendChild(child)
      }
      return frag
    }
    _compile(node) {
      if (node.nodeType === 1) { //标签节点
        const attrs=node.attributes
        if (attrs.hasOwnProperty('v-model')) {
          const name = attrs['v-model'].nodeValue
          node.addEventListener('input', e => {
            this.vm[name] = e.target.value;
          })
        }
      }
      if (node.nodeType === 3) { //文本节点
        console.log(reg.test(node.nodeValue));
        if (reg.test(node.nodeValue)) {
          const name=RegExp.$1.trim()
          console.log(name);
          new Watcher(node, name, this.vm)
        }
      }
    }
  }
</script>
<!--原生的Vue-->
<script src="./node_modules/vue/dist/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: 'Vue响应式',
      info: {
        name: '君莫邪',
        age: 20
      }
    }
  })
</script>
</body>
</html>
